Apache Commons IO简介


之前一直学的都是Java中IO的知识,使用起来基本上都是那个一个样,说实话我确实是有点儿感到厌倦了。于是世界上著名的开源组织阿帕奇(Apache)将Java中的IO进行了封装写了Commons IO这个玩意。这个是需要从阿帕奇的光网上面去下载的,因为这个并不是jdk自带的库。下载完之后还自带了源码以及官方文档,不过都是英文的,,这就非常考验我的能力了。

先看一下这个Commons IO 由哪些东西组成。

org.apache.commons.io    
// This package defines utility classes for working with streams, readers, writers and files.
org.apache.commons.io.comparator    
// This package provides various Comparator implementations for Files.
org.apache.commons.io.filefilter    
// This package defines an interface (IOFileFilter) that combines both FileFilter and FilenameFilter.
org.apache.commons.io.input    
// This package provides implementations of input classes, such as InputStream and Reader.
org.apache.commons.io.monitor    
// This package provides a component for monitoring file system events (directory and file create, update and delete events).
org.apache.commons.io.output    
// This package provides implementations of output classes, such as OutputStream and Writer.
org.apache.commons.io.serialization    
// This package provides a framework for controlling the deserialization of classes.

上面的这所只言片语,说实话我是什么都看不懂的,如果不是对Commons IO的体系有足够的了解,是肯定看不懂的。就像给我们一开始看那个Java io的体系图,我们也肯定是看的一脸懵逼的吖~~

而且上面的并不是一些类而已,只是一些包,每个包中还有非常多的类,每个类中又有非常多的方法。所以说不可能一个一个去学习的,只能寻找自己需要的去学,这里就看一下别人的博客,毕竟我对这个玩意是一点也不了解。

Apache Commons IO入门教程

这个博客写了一点,但是我感觉写的并不怎么样,看了也没什么收获。下面我就直接上一些这个类的常用方法的代码吧。

文件或者文件夹的大小

public class TestCommonIo {
    public static void main(String[] args) {
        long len1 = FileUtils.sizeOf(new File("test"));
        long len2 = FileUtils.sizeOf(new File("./Exercise/src"));
        System.out.println(len1);
        System.out.println(len2);
    }
}

上面的第一个就是文件的大小,第二个是文件夹的大小,使用的方法是一样的。对于计算一个文件的大小,这还是非常简单的,使用length()方法就可以了,不过计算文件夹的代码确实要进行递归的操作,可以说要考虑的东西很多,而且也很繁琐。

我们可以看一下这个方法的源码

public static long sizeOf(final File file) {

    if (!file.exists()) {
        final String message = file + " does not exist";
        throw new IllegalArgumentException(message);
    }

    if (file.isDirectory()) {
        return sizeOfDirectory0(file); // private method; expects directory
    } else {
        return file.length();
    }

}

可以看到源码中专门写了一个函数来计算文件夹的大小

private static long sizeOfDirectory0(final File directory) {
    final File[] files = directory.listFiles();
    if (files == null) {  // null if security restricted
        return 0L;
    }
    long size = 0;

    for (final File file : files) {
        try {
            if (!isSymlink(file)) {
                size += sizeOf0(file); // internal method
                if (size < 0) {
                    break;
                }
            }
        } catch (final IOException ioe) {
            // Ignore exceptions caught when asking if a File is a symlink.
        }
    }

    return size;
}

private static long sizeOf0(final File file) {
    if (file.isDirectory()) {
        return sizeOfDirectory0(file);
    } else {
        return file.length(); // will be 0 if file does not exist
    }
}

简单的看一下确实也是使用递归的方法来计算文件夹的大小的。

列出文件

static void test2() {
    Collection<File> files = FileUtils.listFiles(
            new File("./Exercise/src"), EmptyFileFilter.NOT_EMPTY, null);
    for (File file : files) {
        System.out.println(file.getName());
    }
}

listFiles函数的第一个参数是待列的文件夹,第二个参数是文件过滤器(这里使用的是非空过滤器),第三个参数是文件夹过滤器(使用null就是表示不列出文件夹)

static void test2() {
    Collection<File> files = FileUtils.listFiles(
            new File("./Exercise/src"), EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);
    for (File file : files) {
        System.out.println(file.getName());
    }
}

使用这个方法就可以列出所有的文件,包括文件夹以及文件夹里面的内容。还有一种常用的文件过滤器是后缀名过滤。比如说我只想要后缀为Java的非空的文件,可以这样写。

static void test2() {
    Collection<File> files = FileUtils.listFiles(
            new File("./Exercise/src"), 
            FileFilterUtils.and(new SuffixFileFilter("java"), EmptyFileFilter.NOT_EMPTY), 
            DirectoryFileFilter.INSTANCE);

    for (File file : files) {
        System.out.println(file.getName());
    }
}

可以使用FileFilterUtils.and,FileFilterUtils.or或者FileFilterUtils.or来组织过滤器之间的逻辑,比如上面的就是以java为后缀名的并且非空的文件。

读取内容

static void test3() {
    try {
        String msg = FileUtils.readFileToString(new File("hony"), StandardCharsets.UTF_8);
        System.out.println(msg);

        byte[] b = FileUtils.readFileToByteArray(new File("hony"));
        System.out.println(new String(b, StandardCharsets.UTF_8));

        List<String> lines = FileUtils.readLines(new File("hony"), StandardCharsets.UTF_8);
        for (String line : lines) {
            System.out.println(line);
        }

        LineIterator lineIterator = FileUtils.lineIterator(new File("hony"), "UTF-8");
        while (lineIterator.hasNext()) {
            System.out.println(lineIterator.nextLine());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

读取文件中的内容主要有上面的四种方式,无论哪一种都是比我们使用原始的Java IO流方便多了。

第一种是直接将文件中的内容读取到一个String对象中。第二种则是读取到字节数组中。

第三种是将文件的每一行都放入到一个List中。第四种方式倒是有点儿特殊,是使用迭代器的方式进行遍历文件的。

写出内容

static void test4() {
    try {
        FileUtils.write(new File("happy.txt"),
                "I am happy1!", StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File("happy.txt"),
                "I am happy2!", StandardCharsets.UTF_8, true);
        FileUtils.writeByteArrayToFile(new File("happy.txt"),
                "I am happy3!".getBytes(StandardCharsets.UTF_8), true);

        List<String> datas = new ArrayList<>();
        datas.add("sher");
        datas.add("hony");
        datas.add("sherhony");

        FileUtils.writeLines(new File("happy.txt"),
                datas, "\r\n", true);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

写出到文件中同样有以上的好几种方式。上面函数的第四个参数都是代表是否一追加的形式写入到文件当中,在Java原始流当中也是使用的这种方式。不过下面的FileUtils.writeLines方法的第三个参数不再是编码了,而是分隔符,也就是说写入的这些data之间用什么隔开,这里我是用的是windows下的换行符。

拷贝

拷贝包括拷贝文件或者说的是拷贝文件夹,拷贝文件的操作我们已经做过了,但是拷贝文件夹的操作是有点儿复杂,需要递归的操作。不过这个commons io提供给我们一个十分方便地方法。

static void test5() {
    try {
        FileUtils.copyFile(new File("Exercise/src/test.jpg"),
                new File("Exercise/src/test-copy.jpg"));
        FileUtils.copyFileToDirectory(new File("Exercise/src/test.jpg"),
                new File("D:/"));
        FileUtils.copyDirectory(new File("Exercise/out"), new File("Exercise/out-copy"));
        FileUtils.copyDirectoryToDirectory(new File("Exercise/out"),
                new File("Exercise/src"));
        String url = "http://i1.hdslb.com/bfs/archive/9cc5e3fcc7d6c48e5a0d8d3dd8bbb6ed11937f89.jpg";
        FileUtils.copyURLToFile(new URL(url), new File("Exercise/src/miaonei.jpg"));
        String baidu = "http://www.baidu.com";
        String wangyi = "http://www.163.com";
        String bd = IOUtils.toString(new URL(baidu), StandardCharsets.UTF_8);
        String wy = IOUtils.toString(new URL(wangyi), "GBK");
        FileUtils.writeStringToFile(new File("Exercise/src/baidu.txt"), bd,
                StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File("Exercise/src/wangyi.txt"), wy,
                "GBK");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

上面是拷贝的常见的操作。可以看到这个方法是什么的方便的。不过需要注意的是,从网络流中拷贝东西(也就是下载东西)的时候,如果是文字的话(也就网页的代码),可以使用IOUtils.toString方法,这个方法可以指定字符的编码。

总结

上面就是Commons IO中简单的常用的一些操作,不过Commons IO这么强大的库怎么可能就这么一点点功能,只不过其余的功能太过与强大,而且并不是经常用到,所以现在可以不用了解。现在就是简单的看看这个类,知道有这么个玩意就行了。


一枚小菜鸡